﻿using System;
using System.ComponentModel.Design;
using System.Globalization;
using System.IO;
using Microsoft.WowAddonStudio.FrameXml.Components.Events.ScriptManager;
using Microsoft.WowAddonStudio.FrameXml.Components.Layout;
using Microsoft.WowAddonStudio.FrameXml.Components.Support;
using Microsoft.WowAddonStudio.FrameXml.Formats.Mpq;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Support;
using Microsoft.WowAddonStudio.FrameXml.Services;

namespace Microsoft.WowAddonStudio.FrameXml.Design
{
	public class FrameXmlDesignerFactory
	{
		private const string mpqFolderRelativePath = "Data";

		/// <summary>
		/// Gets or sets the World of Warcraft base path.
		/// </summary>
		public string WowBasePath { get; set; }

		/// <summary>
		/// Creates a design surface for designing FrameXML user interfaces.
		/// </summary>
		/// <returns>An instance of the <see cref="DesignSurface"/> class.</returns>
		public DesignSurface CreateDesignSurface()
		{
			// Create service container
			var serviceContainer = CreateServiceContainer();

			// Create design surface using the service container
			return new FrameXmlDesignSurface(serviceContainer);
		}

		/// <summary>
		/// Creates the service container.
		/// </summary>
		/// <returns></returns>
		private IServiceContainer CreateServiceContainer()
		{
			// Create service container
			var serviceContainer = new ServiceContainer();

			// Create resource file loader used by FrameXML services
			var resourceFileLoader = CreateResourceFileLoader();

			// Add FrameXML layout engine
			serviceContainer.AddService(typeof(FrameXmlLayoutEngine), new FrameXmlLayoutEngine());

			// Add FrameXML services
			serviceContainer.AddService(typeof (FrameXmlFontService), new FrameXmlFontService(resourceFileLoader));
			serviceContainer.AddService(typeof (FrameXmlImageService), new FrameXmlImageService(resourceFileLoader));

			// Add FrameXML loader service
			serviceContainer.AddService(typeof (FrameXmlLoader), new FrameXmlLoader());

			IEventBindingService eventService = new FrameXmlEventBindingService(serviceContainer);
			serviceContainer.AddService(typeof(IEventBindingService), eventService);

			IFrameXmlScriptManager scriptManager = new FrameXmlScriptManager(serviceContainer);
			serviceContainer.AddService(typeof(IFrameXmlScriptManager), scriptManager);

			// Return the service container
			return serviceContainer;
		}

		/// <summary>
		/// Creates the resource file loader.
		/// </summary>
		/// <returns></returns>
		private ResourceFileLoader CreateResourceFileLoader()
		{
			if (!Directory.Exists(WowBasePath))
				throw new InvalidOperationException("Invalid World of Warcraft base path.");

			var resourceFileLoader = new ResourceFileLoader();

			// Add WoW base path to resource file loader
			resourceFileLoader.BasePaths.Add(WowBasePath);

			string mpqFolderPath = Path.Combine(WowBasePath, mpqFolderRelativePath);

			// Find the installed locale folder under the MPQ folder
			string[] mpqDirectories = Directory.GetDirectories(mpqFolderPath);

			// Make sure the directory is available
			if (mpqDirectories.Length > 0)
			{
				// Use the first directory
				string localeDirectory = mpqDirectories[0];
				
				// Look up patch MPQs
				var patchMpqPathFilter = String.Format(CultureInfo.InvariantCulture, "patch-{0}*.MPQ", localeDirectory.Substring(localeDirectory.LastIndexOf("\\") + 1, localeDirectory.Length - localeDirectory.LastIndexOf("\\") - 1));
				var patchMpqs = Directory.GetFiles(localeDirectory, patchMpqPathFilter);

				// Sort and reverse the patch MPQs
				Array.Sort(patchMpqs);
				Array.Reverse(patchMpqs);

                // Add patch MPQs to resource file loader in reverse order
				foreach (var patchMpq in patchMpqs)
				{
					// Construct path to patch MPQ archive
					string patchMpqArchivePath = Path.Combine(localeDirectory, patchMpq);

					// Validate patch MPQ and add it to resource file loader
					if (MpqArchive.IsValidMpqArchive(patchMpqArchivePath))
						resourceFileLoader.MpqArchives.Add(new MpqArchive(patchMpqArchivePath));
				}

				// Construct path to locale-specific MPQ archive
				string mpqArchivePath = Path.Combine(localeDirectory, String.Format("locale-{0}.MPQ", Path.GetFileName(localeDirectory)));

				// Validate locale-specific MPQ and add it to resource file loader
				if (MpqArchive.IsValidMpqArchive(mpqArchivePath))
					resourceFileLoader.MpqArchives.Add(new MpqArchive(mpqArchivePath));
			}

			return resourceFileLoader;
		}
	}
}